﻿using System;
using System.IO; 
using System.Text;
using System.Text.RegularExpressions;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Yanesdk.System;
using Yanesdk.Input;
using Yanesdk.Draw;
using Yanesdk.Movie;
using Yanesdk.Sound;
using Yanesdk.Ytl;
using WMPLib;
using ADV_Evaluator.Data;
using ADV_Evaluator.Command;

using YD = Yanesdk.Draw;
using SD = System.Drawing;
using System.Collections;
using Yanesdk.GUIParts;


namespace ADV_Evaluator.Scene {

    /// <summary>
    /// 
    /// </summary>
    class MainScene : TaskBase, IDisposable {

        private GameSystem system;
        private VariableList variableList;
        private Window_Message messageWindow;
        private Window_Choice choiceWindow;
		private Window_Menu menuWindow;
		private SpriteList[] spriteArray;
        private MySprite bgSprite;
        private MySprite bgSpriteFront;
        private MySprite bgSystemSprite;
        private Bitmap bgSystemBitmap;
        private Graphics bgSystemGraphics;
        private GlTexture capturedTexture;
        //private Sound bgm;    // なんか調子悪い・・・・
        private Sound se;
        private WindowsMediaPlayer bgm;
        //private WindowsMediaPlayer se;
        private Movie movie;
        private GlTexture movieTexture;
        private List<CommandData> commandList;
        private int commandIndex;
        private int waitCount;
        private int transitionDuration, transitionCount;
        private bool transitionPreparing;
        private bool keyWaiting;
		private bool clickableWaiting;		//クリッカブル領域のクリック待ち
		private bool menuWaiting;			//メニュー表示の終了待ち
		private TextureGUIControl control;
		private MessageOptionParams SaveMessageOption;
		private Color SaveBackColor;
		private bool SaveBgmStart = false;


		/// <summary>
        /// コンストラクタ
        /// </summary>
        public MainScene(GameSystem system) {
            this.system = system;
            this.variableList = new VariableList();
            this.messageWindow = new Window_Message(WindowAlign.Bottom, system.Screen.Width, system.Screen.Height);
            this.choiceWindow = new Window_Choice(system.Screen.Width, system.Screen.Height);
			this.menuWindow = new Window_Menu(system.Screen.Width, system.Screen.Height);
			this.control = new TextureGUIControl();
			ControlContext cc = new ControlContext();
			cc.MouseInput = system.Mouse;
			cc.SmartTextureLoader = system.SmartTextureLoader;
			cc.SmartSoundLoader = system.SmartSoundLoader;
			cc.Param = null;
			control.OnInit(cc);
            this.spriteArray = new SpriteList[system.ProjectData.LayerSlot];
			for (int i = 0; i < system.ProjectData.LayerSlot; i++)
			{
				spriteArray[i] = new SpriteList();
			}
            this.bgSprite = new MySprite();
            this.bgSpriteFront = new MySprite();
            this.bgSystemSprite = new MySprite();
            this.bgSystemBitmap = new Bitmap(system.Screen.Width, system.Screen.Height);
            this.bgSystemGraphics = Graphics.FromImage(bgSystemBitmap);
            this.capturedTexture = new GlTexture();
            this.bgm = new WindowsMediaPlayer();
            this.bgm.settings.playCount = int.MaxValue;  // 無限ループのつもり。厳密には2147483647回繰り返し再生。1秒のBGMでも68年かかる計算だから大丈夫だろう・・・
            this.se = new Sound();
            this.movie = new Movie();
            this.movieTexture = new GlTexture();
            this.movieTexture.SetSurface(this.movie.Surface);
            this.waitCount = -1;
            this.transitionCount = -1;
            this.transitionPreparing = false;
            this.keyWaiting = false;
			this.clickableWaiting = false;

            // シナリオデータを読み込む。失敗したら終了
            if (!LoadScenarioData(system.ProjectData.InitialScenario)) return;

            // 最初のメッセージを表示
            Evaluate();
        }

        /// <summary>
        /// シナリオデータ配列を*.scnファイルから読み込み、返す
        /// </summary>
        /// <param name="filename">読み込むファイル名</param>
        /// <returns>読み込みに失敗したらnull</returns>
        private List<CommandData> GetScenarioData(string filename) {
            FileStream fs = null;
            List<CommandData> data = null;
            try {
                fs = new FileStream("シナリオ/" + filename, FileMode.Open, FileAccess.Read);
                BinaryFormatter bf = new BinaryFormatter();
                // 読み込んで逆シリアル化する
                data = new List<CommandData>(bf.Deserialize(fs) as CommandData[]);
            // 読み込めなければ終了
            } catch (Exception e) {
                system.Error(e.ToString());
            } finally {
                if (fs != null) {
                    fs.Close();
                }
            }
            return data;
        }

        /// <summary>
        /// シナリオファイルを読み込み、そのデータで this.commandList を初期化する。
        /// </summary>
        /// <param name="filename"></param>
        /// <returns>初期化に成功すればtrue</returns>
        private bool LoadScenarioData(string filename) {
            commandList = GetScenarioData(filename);
            commandIndex = -1;
            return (commandList != null);
        }

        /// <summary>
        /// 後始末
        /// </summary>
        public void Dispose() {
            messageWindow.Dispose();
            choiceWindow.Dispose();
			menuWindow.Dispose();
            spriteArray.Initialize();
            bgSprite.Dispose();
            bgSpriteFront.Dispose();
            bgSystemSprite.Dispose();
            capturedTexture.Dispose();
            se.Dispose();
            bgm.controls.stop();
			control.Dispose();
		}

        /// <summary>
        /// 次のメッセージの表示までシナリオデータを解釈する
        /// </summary>
        private void Evaluate() {
            try {

                int loopCount = 0;

                // 終端が来るか、文章、フェード、ウェイトなどの一旦解釈をとめるべきコマンドが来るまで繰り返し
                while(true){

                    // whileループが回りすぎていれば、無限ループに入った恐れがあるので中断
                    loopCount++;
                    if (loopCount > 10000) {
                        throw new Exception("無限ループに入った可能性があります。");
                    }

                    commandIndex++;
                    // 終端まで読み込んでいたら終了
                    if (commandIndex >= commandList.Count) {
                        system.TaskController.Terminate();
                        return;
                    }

                    CommandName commandName = commandList[commandIndex].Name;  // コマンド名
                    ICommandParams param = commandList[commandIndex].Params; // コマンド全属性
                    byte indent = commandList[commandIndex].Indent;

                    // コマンド名ごとに分岐
                    switch (commandName) {

                    case CommandName.文章の表示:
                        {
                            MessageParams p = (MessageParams)param;
                            string text = p.Text;
                            // 正規表現にマッチした部分をどう置換するかを決めるデリゲート
                            MatchEvaluator evaluator = delegate(Match m) {
                                string varName = m.Groups[1].Value;
                                // 変数が定義済みなら変数の値を、未定義ならそのまま変数名を返す
                                if (variableList.Exists(varName)) {
                                    return variableList[varName].ToString();
                                } else {
                                    return varName;
                                }
                            };
                            // 変数埋め込み記法を変数の値に置換
                            text = Regex.Replace(text, @"\#\{(\w+)\}", evaluator, RegexOptions.Compiled);
                            messageWindow.Show(text);
                            // トランジション中でなければ解釈停止
                            if (!transitionPreparing) {
                                keyWaiting = true;
                                return;
                            // トランジション中なら解釈を続行
                            } else {
                                break;
                            }
                        }
                    case CommandName.文章オプションの変更: 
                        {
                            MessageOptionParams p = (MessageOptionParams)param;
                            messageWindow.SetParams(p, system.Screen.Height);
                            choiceWindow.SetParams(p);
							// TODO:暫定対応
							menuWindow.SetParams(p);
							SaveMessageOption = p;
                        }
                        break;

                    case CommandName.選択肢の表示: 
                        {
                            ChoiceParams p = (ChoiceParams)param;
                            choiceWindow.Show(p.Options);
                            // トランジション中なら中止
                            if (transitionPreparing) {
                                transitionPreparing = false;
                                transitionDuration = transitionCount = -1;
                                capturedTexture.Release();
                            }
                            // 解釈を中断し、プレイヤーの選択を待つ
                            return;                           
                        }

                    case CommandName.選択肢: 
                        {
                            // ここに来るのは1つの選択肢の処理を終えて流れ落ちてきたときだけなので、分岐終了まで飛んでしまう
                            ChoiceOptionParams p = (ChoiceOptionParams)param;
                            for (int i = commandIndex + 1; i < commandList.Count; i++) {
                                CommandData c = commandList[i];
                                if (c.Name == CommandName.分岐終了 && c.Indent == indent) {
                                    commandIndex = i;
                                    break;
                                }
                            }
                        }
                        break;

                    case CommandName.条件分岐: {
                            BranchParams p = (BranchParams)param;
                            // 条件式が成り立ったとき
                            if (p.CheckCondition(variableList[p.VariableName])) {
                                // そのまま次の行へ

                                // 条件式が成り立たなかったとき
                            } else {
                                // else節があれば"それ以外の場合"、無ければ"分岐終了" の次の行までインデックスを進めてしまう
                                CommandName target = p.HasElse ? CommandName.それ以外の場合 : CommandName.分岐終了;
                                for (int i = commandIndex + 1; i < commandList.Count; i++) {
                                    if (commandList[i].Name == target && commandList[i].Indent == indent) {
                                        commandIndex = i;
                                        break;
                                    }
                                }
                            }

                        }
                        break;

                    case CommandName.それ以外の場合:
                        // このラベルに来るのは、if節を実行した後だけなので、以下分岐終了までをスキップする
                        for (int i = commandIndex + 1; i < commandList.Count; i++) {
                            if (commandList[i].Name == CommandName.分岐終了 && commandList[i].Indent == indent) {
                                commandIndex = i;
                                break;
                            }
                        }
                        break;

                    case CommandName.分岐終了:
                        break;

                    case CommandName.ループ: 
                        {
                            LoopParams p = (LoopParams)param;
                            // 条件式があるとき
                            if (p.HasCondition) {
                                // 条件が成り立たなければ、ループ終了までジャンプ
                                if (!p.CheckCondition(variableList[p.VariableName])) {
                                    for (int i = commandIndex + 1; i < commandList.Count; i++) {
                                        if (commandList[i].Name == CommandName.以上繰り返し && commandList[i].Indent == indent) {
                                            commandIndex = i;
                                            break;
                                        }
                                    }
                                }
                            }
                            // 条件式が無ければ、普通に次へ進む
                        }
                        break;

                    case CommandName.以上繰り返し: 
                        {
                            // インデックスを上に探していって、インデントが同じ最も近いループ開始行へ移動
                            for (int i = commandIndex - 1; i >= 0; i--) {
                                if (commandList[i].Name == CommandName.ループ && commandList[i].Indent == indent) {
                                    // ループに条件があったときのために、次回もループ開始行を解釈させるため1つ手前へ戻す
                                    commandIndex = i - 1;
                                    break;
                                }
                            }
                        }
                        break;

                    case CommandName.ループを抜ける: 
                        {
                            // 一番近いループ終了行を下に探していってジャンプ
                            // このコマンドはループの中でなくても挿入できてしまうので、そのときは無視する
                            if (indent == 0) {
                                break;  // インデント0はありえない
                            }
                            for (int i = commandIndex + 1; i < commandList.Count; i++) {
                                if (commandList[i].Name == CommandName.以上繰り返し) {
                                    commandIndex = i - 1;
                                    break;
                                }
                            }
                        }
                        break;

                    case CommandName.シナリオの移動: 
                        {
                            ScenarioParams p = (ScenarioParams)param;
                            // 対象シナリオを読み込んで、データを置き換えてしまう。
                            // 読み込みに失敗したら終了。
                            if (!LoadScenarioData(p.FileName)) {
                                return;
                            }
                        }
                        break;

                    case CommandName.シナリオを埋め込む: 
                        {
                            ScenarioParams p = (ScenarioParams)param;
                            // 挿入するコマンド列を取得
                            List<CommandData> includeData = GetScenarioData(p.FileName);
                            if (includeData != null) {
                                // ラベルジャンプで戻ってきても増えてしまわないように、この行を消して置換してしまう
                                commandList.RemoveAt(commandIndex);
                                commandList.InsertRange(commandIndex, includeData);
                                // このままでは挿入した行の最初の行が解釈されないので、もう一回今の位置を解釈するようにひとつ戻す
                                commandIndex--;
                            }
                        }
                        break;

					case CommandName.クリッカブル領域の配置:
						{
							ClickableMapParams p = (ClickableMapParams)param;
                            if (control.Controls.Contains(control.GetControl(p.Label))) {
                                break;
                            }
							#region ファイルの存在確認
                            if (!File.Exists("画像/ボタン/" + p.ImageFileNameNormal)) {
                                throw new FileNotFoundException("ファイル " + p.ImageFileNameNormal + " は存在しません。", p.ImageFileNameNormal);
                            }
                            if (!File.Exists("画像/ボタン/" + p.ImageFileNamePush)) {
                                throw new FileNotFoundException("ファイル " + p.ImageFileNamePush + " は存在しません。", p.ImageFileNamePush);
                            }
                            if (!File.Exists("画像/ボタン/" + p.ImageFileNameHover)) {
                                throw new FileNotFoundException("ファイル " + p.ImageFileNameHover + " は存在しません。", p.ImageFileNameHover);
                            }
                            if (!File.Exists("画像/ボタン/" + p.ImageFileNameDisable)) {
                                throw new FileNotFoundException("ファイル " + p.ImageFileNameDisable + " は存在しません。", p.ImageFileNameDisable);
                            }
							#endregion
							TextureButton button = new TextureButton("MEM:画像/ボタン/" + p.ImageFileNameNormal + Environment.NewLine + "画像/ボタン/" + p.ImageFileNamePush + Environment.NewLine + "画像/ボタン/" + p.ImageFileNameHover + Environment.NewLine + "画像/ボタン/" + p.ImageFileNameDisable);
                            #region ボタンイベントの設定
                            button.OnClick += delegate(IScreen s, ControlContext c) {
                                clickableWaiting = false;
								switch (p.DeleteMode)
								{
									case DeleteClickableMapMode.Single:
										if (control.Controls.Contains(control.GetControl(p.DeleteLabel)))
										{
											control.RemoveControl(p.DeleteLabel);
											if (control.Controls.Count.Equals(0))
											{
												clickableWaiting = false;
											}
										}
										break;
									case DeleteClickableMapMode.All:
										control.Controls.Clear();
										clickableWaiting = false;
										break;
								}
								switch (p.Mode)
								{
                                case ClickableMapMode.Scenario:
                                    if (!LoadScenarioData(p.ScenarioFileName)) {
										return;
                                    }
									Evaluate();
									break;
                                case ClickableMapMode.Label:
                                    for (int i = 0; i < commandList.Count; i++) {
                                        if (commandList[i].Name == CommandName.ラベルの定義) {
                                            if (((LabelParams)commandList[i].Params).Name == p.ScenarioFileName) {
                                                commandIndex = i;
                                                Evaluate();
                                                break;
                                            }
                                        }
                                    }
                                    break;
                                }
                            };
							#endregion
							control.AddControl(button, p.X, p.Y, p.Label);
						}
						break;

					case CommandName.クリッカブル領域の削除:
						{
							DeleteClickableMapParams p = (DeleteClickableMapParams)param;
                            switch (p.Mode) {
                            case DeleteClickableMapMode.Single:
                                if (control.Controls.Contains(control.GetControl(p.Label))) {
                                    control.RemoveControl(p.Label);
                                    if (control.Controls.Count.Equals(0)) {
                                        clickableWaiting = false;
                                    }
                                }
                                break;
                            case DeleteClickableMapMode.All:
								control.Controls.Clear();
								clickableWaiting = false;
                                break;
							}
						}
						break;

                    case CommandName.立ち絵の消去: 
                        {
                            ImageParams p = (ImageParams)param;
                            if (!spriteArray[p.Z].ContainsKey(p.Label)) {
                                break;
                            } 
                            // トランジション準備中ではなく、かつフレーム数が指定されていればフェードアウト実行
                            if (!transitionPreparing && p.Duration > 0) {
                                spriteArray[p.Z][p.Label].Fadeout(p.Duration);
                                // フェードアウトが終わったら解放し、次のメッセージを表示
                                spriteArray[p.Z][p.Label].FadeEnded = delegate {
                                    spriteArray[p.Z].Remove(p.Label);
                                    Evaluate();
                                };
                                return;
                                // 普通に消去する
                            } else {
                                spriteArray[p.Z].Remove(p.Label);
                            }
                        }
                        break;

                    case CommandName.立ち絵の表示: 
                        {
                            ImageParams p = (ImageParams)param;
							bool publish = false;
                            if (!File.Exists("画像/立ち絵/" + p.FileName)) {
								string name = p.FileName.Substring(0, p.FileName.Length - 3) + Const.ImageEx;
								if (!File.Exists("画像/立ち絵/" + name))
								{
	                                throw new FileNotFoundException("ファイル " + p.FileName + " は存在しません。", p.FileName);
								}
								else
								{
									p.FileName = name;
									publish = true;
								}
                            }                            
                            // 未定義のラベルなら、新規作成
                            if (!spriteArray[p.Z].ContainsKey(p.Label)) {
                                spriteArray[p.Z].Add(p.Label, new MySprite());
                            // 既に存在するのならリストの末尾に持ってくる（z座標を最大にする）
                            } else {
                                spriteArray[p.Z].ShiftToLast(p.Label);
                            }
                            // 読み込む
                            MySprite s = spriteArray[p.Z][p.Label];
							if (publish)
							{ s.Load_Bitmap(DataConverter.DataDecode("画像/立ち絵/" + p.FileName)); }
							else
							{ s.Load("画像/立ち絵/" + p.FileName); }
                            s.Alpha = 255;
                            // 既定の位置[左・中央・右]で指定されているときは、既定の位置にセット
                            switch (p.Label) {
                            case "__left__":
                                s.X = (system.Screen.Width / 6) + (system.Screen.Width / 3 * 0) - (s.Width / 2);
                                s.Y = (system.Screen.Height / 3) - (s.Height / 2);
                                break;
                            case "__center__":
                                s.X = (system.Screen.Width / 6) + (system.Screen.Width / 3 * 1) - (s.Width / 2);
                                s.Y = (system.Screen.Height / 3) - (s.Height / 2);
                                break;
                            case "__right__":
                                s.X = (system.Screen.Width / 6) + (system.Screen.Width / 3 * 2) - (s.Width / 2);
                                s.Y = (system.Screen.Height / 3) - (s.Height / 2);
                                break;
                            // それ以外は指定された座標に
                            default:
                                s.X = p.X;
                                s.Y = p.Y;
                                break;
                            }
                            // トランジション準備中ではなく、かつフレーム数が指定されていればフェードイン実行
                            if (!transitionPreparing && p.Duration > 0) {
                                s.Fadein(p.Duration);
                                // フェードが終わったら次へ進める
                                s.FadeEnded = delegate { 
                                    Evaluate();
                                };
                                return;
                            }                            
                        }
                        break;

                    case CommandName.背景画像の消去: {
                            BgImageParams p = (BgImageParams)param;
                            if (!transitionPreparing && p.Duration > 0) {
                                if (p.Z.Equals(0)) {
                                    bgSprite.Fadeout(p.Duration);
                                    bgSprite.FadeEnded = delegate {
                                        bgSprite.Release();
                                        Evaluate();
                                    };
                                } else {
                                    bgSpriteFront.Fadeout(p.Duration);
                                    bgSpriteFront.FadeEnded = delegate {
                                        bgSpriteFront.Release();
                                        Evaluate();
                                    };
                                }
                                return;
                            } else {
                                if (p.Z.Equals(0)) {
                                    bgSprite.Release();
                                } else {
                                    bgSpriteFront.Release();
                                }
                            }
                        }
                        break;

                    case CommandName.背景画像の表示: {
                            BgImageParams p = (BgImageParams)param;
							bool publish = false;
                            if (!File.Exists("画像/背景/" + p.FileName)) {
								string name = p.FileName.Substring(0, p.FileName.Length - 3) + Const.ImageEx;
								if (!File.Exists("画像/背景/" + name))
								{
									throw new Exception("ファイル " + p.FileName + " は存在しません。");
								}
								else
								{
									p.FileName = name;
									publish = true;
								}
                            }
                            if (p.Z.Equals(0)) {
								if (publish)
								{ bgSprite.Load_Bitmap(DataConverter.DataDecode("画像/背景/" + p.FileName)); }
								else
								{ bgSprite.Load("画像/背景/" + p.FileName); }
                                bgSprite.Alpha = 255;
                                bgSprite.DestSize.SetSize(system.Screen.Width, system.Screen.Height);
                                if (!transitionPreparing && p.Duration > 0) {
                                    bgSprite.Fadein(p.Duration);
                                    bgSprite.FadeEnded = delegate {
                                        Evaluate();
                                    };
                                    return;
                                }
                            } else {
                                bgSpriteFront.Load("画像/背景/" + p.FileName);
                                bgSpriteFront.Alpha = 255;
                                bgSpriteFront.DestSize.SetSize(system.Screen.Width, system.Screen.Height);
                                if (!transitionPreparing && p.Duration > 0) {
                                    bgSpriteFront.Fadein(p.Duration);
                                    bgSpriteFront.FadeEnded = delegate {
                                        Evaluate();
                                    };
                                    return;
                                }
                            }
                        }
                        break;

					case CommandName.背景色の変更:
						{
							BackColorParams p = (BackColorParams)param;
							SaveBackColor = p.BackColor;
							// フレーム数が指定されていなければ即座に実行
                            if (p.Duration.Equals(0)) {
                                system.Screen.SetClearColor(p.BackColor.R, p.BackColor.G, p.BackColor.B);
								break;
							}                            
                            // 読み込む
                            bgSystemGraphics.FillRectangle(new SolidBrush(p.BackColor), bgSystemGraphics.VisibleClipBounds);
                            bgSystemSprite.Load_Bitmap(bgSystemBitmap);
                            bgSystemSprite.Alpha = 0;
                            bgSystemSprite.DestSize.SetSize(system.Screen.Width, system.Screen.Height);
                            // トランジション準備中ではなく、かつフレーム数が指定されていればフェードイン実行
                            if (!transitionPreparing && p.Duration > 0) {
                                bgSystemSprite.Fadein(p.Duration);
                                // フェードが終わったら次へ進める
                                bgSystemSprite.FadeEnded = delegate {
                                    system.Screen.SetClearColor(p.BackColor.R, p.BackColor.G, p.BackColor.B);
                                    bgSystemSprite.Release();
                                    Evaluate();
                                };
                                return;
                            }
						}
						break;
					case CommandName.トランジション準備: 
                        {
                            transitionPreparing = true;
                            // 現在のスクリーンに映っている画像を取得し、トランジション実行まではそれだけを表示するようにする
                            capturedTexture.SetSurface(system.Screen.GetSurface());
                        }
                        break;
                    case CommandName.トランジション実行:
                        {
                            TransitionParams p = (TransitionParams)param;
                            transitionPreparing = false;
                            transitionDuration = transitionCount = p.Duration;
                        }
                        return;
                    case CommandName.ＢＧＭの停止:
                        bgm.controls.stop();
						SaveBgmStart = false;
                        break;
                    case CommandName.ＢＧＭの演奏:
                        {
                            BgmParams p = (BgmParams)param;
                            if (!File.Exists("サウンド/BGM/" + p.FileName)) {
                                throw new FileNotFoundException("ファイル " + p.FileName + " は存在しません。", p.FileName);
                            }
                            // ファイル名が違うときだけ読み込む
                            if (Path.GetFileName(bgm.URL) != p.FileName) {
                                bgm.URL = "サウンド/BGM/" + p.FileName;
                                bgm.controls.play();
								SaveBgmStart = true;
                            }
                        }
                        break;
                    case CommandName.ＳＥの停止:
                        se.Release();
                        break;
                    case CommandName.ＳＥの演奏: 
                        {
                            SeParams p = (SeParams)param;
                            if (!File.Exists("サウンド/SE/" + p.FileName)) {
                                throw new FileNotFoundException("ファイル " + p.FileName + " は存在しません。", p.FileName);
                            }
                            se.Load("サウンド/SE/" + p.FileName, 0);
                            se.Play();
                        }
                        break;
                    case CommandName.変数の操作: 
                        {
                            VariableParams p = (VariableParams)param;
                            // オペランドの算出
                            short operand = 0;
                            switch(p.OperandType){
                            case VariableOperand.定数:
                                operand = (short)p.OperandValue;
                                break;
                            case VariableOperand.変数:
                                operand = (short)variableList[(string)p.OperandValue];
                                break;
                            case VariableOperand.乱数:
                                short[] range = (short[])p.OperandValue;
                                int min = (range[0] < range[1]) ? range[0] : range[1];
                                int max = (range[0] > range[1]) ? range[0] + 1 : range[1] + 1;
                                operand = (short)(system.Rand.GetRand(max - min) + min);
                                break;
                            }
                            // 計算(オーバーフローを無視する)
                            unchecked {
                                switch (p.Operation) {
                                case VariableOperation.代入:
                                    variableList[p.Name] = operand; break;
                                case VariableOperation.加算:
                                    variableList[p.Name] += operand; break;
                                case VariableOperation.減算:
                                    variableList[p.Name] -= operand; break;
                                case VariableOperation.乗算:
                                    variableList[p.Name] *= operand; break;
                                case VariableOperation.除算:
                                    // 0の除算は0ということにする
                                    if (operand == 0) {
                                        variableList[p.Name] = 0;
                                    } else {
                                        variableList[p.Name] /= operand;
                                    }
                                    break;
                                case VariableOperation.剰余:
                                    if (operand == 0) {
                                        variableList[p.Name] = 0;
                                    } else {
                                        variableList[p.Name] %= operand;
                                    }
                                    break;
                                case VariableOperation.累乗:
                                    variableList[p.Name] = (int)Math.Pow(variableList[p.Name], operand);
                                    break;
                                }
                            }
                            break;
                        }
                    case CommandName.ウェイト: 
                        {
                            WaitParams p = (WaitParams)param;
                            switch(p.Mode){
                            case WaitMode.Frame:
                                waitCount = p.Duration;
                                break;
                            case WaitMode.WaitKey:
                                keyWaiting = true;
                                break;
							case WaitMode.WaitClickable:
								clickableWaiting = true;
								break;
                            }
                        }
                        return;
                    case CommandName.ラベルの定義:
                        break;
                    case CommandName.ラベルに飛ぶ: 
                        {
                            // 上からラベルを探していって見つかったらそのインデックスに設定
                            LabelParams p = (LabelParams)param;
                            for (int i = 0; i < commandList.Count; i++) {
                                if (commandList[i].Name == CommandName.ラベルの定義) {
                                    if (((LabelParams)commandList[i].Params).Name == p.Name) {
                                        commandIndex = i;
                                        break;
                                    }
                                }
                            }
                        }
                        break;

                    default:
                        break;
                    }
                }
                
            } catch (Exception e) {
#if DEBUG
                Debug.Assert(false, (commandIndex+2) + "行目でエラーが発生しました。\n\n" + e.ToString());
#else
                system.Error((commandsIndex+2) + "行目でエラーが発生しました。\n\n" + e.Message);
#endif
                return;
            }

        }

        /// <summary>
        /// 選択肢ウィンドウで選ばれた選択肢に従い、目的の選択肢の行までジャンプする
        /// </summary>
        /// <param name="index">選ばれた選択肢インデックス</param>
        private void JumpToOption(int index) {
            int optionCount = 0;
            int indent = commandList[commandIndex].Indent;
            for (int i = commandIndex + 1; i < commandList.Count; i++) {
                CommandData data = commandList[i];
                // インデントが同じ選択肢が来たら
                if (data.Name == CommandName.選択肢 && data.Indent == indent) {
                    // 目的のインデックスかどうか
                    if (index == optionCount) {
                        commandIndex = i;
                        break;
                    } else {
                        optionCount++;
                    }
                // 探索失敗らしい
                } else if (data.Name == CommandName.分岐終了 && data.Indent == indent) {
                    // んなバカな
                    Debug.Assert(false);
                }
            }
        }

        /// <summary>
        /// タスク処理
        /// </summary>
        /// <param name="o">GameSystem</param>
        /// <returns>成功なら0</returns>
        public override int Task(Object o) {
            this.system = o as GameSystem;

            //system.Screen.SetScreenOffset(system.Rand.GetRand(41) - 20, system.Rand.GetRand(41) - 20);

            //if (movie.IsReadyToDraw) {
            //    movie.Draw(movieTexture);
            //    system.Screen.Blt(movieTexture, 0, 0, new Rect(0, 0, movieTexture.Width, movieTexture.Height), new Size(system.Screen.Width, system.Screen.Height));
            //}

            // 立ち絵・背景の描画
            UpdateGraphics();

			if (!menuWaiting)
			{
				// ウェイト中のとき
				if (waitCount >= 0)
				{
					waitCount--;
					if (waitCount < 0)
					{
						Evaluate();
					}

					// トランジション実行中のとき
				}
				else if (transitionCount >= 0)
				{
					transitionCount--;
					if (transitionCount < 0)
					{
						capturedTexture.Release();
						Evaluate();
					}


					// 選択肢ウィンドウが出ているとき
				}
				else if (choiceWindow.IsActive)
				{
					if (system.Form.Focused)
					{
						if (choiceWindow.IsDecided)
						{
							JumpToOption(choiceWindow.SelectedIndex);
							choiceWindow.Hide();
							Evaluate();
						}
					}

					// 文章の表示等でキー入力待ちのとき
				}
				else if (keyWaiting)
				{
					if (system.Form.Focused)
					{
						// マウスを左クリック、もしくはEnter, Space, CTRLキーを押したとき、
						if (system.Mouse.IsPush(MouseInput.Button.Left) ||
							system.Keyboard.IsPush(KeyCode.RETURN) ||
							system.Keyboard.IsPush(KeyCode.SPACE) ||
							system.Keyboard.IsPress(KeyCode.LCTRL) ||
							system.Keyboard.IsPress(KeyCode.RCTRL))
						{
							keyWaiting = false;
							Evaluate();
						}
					}
				}
				// クリッカブル領域のクリック待ち
				else if (clickableWaiting)
				{
					if (system.Form.Focused)
					{
					}
				}
			}

			// メニュー表示の終了
			if (menuWindow.IsActive)
			{
				if (system.Form.Focused)
				{
					if (menuWindow.IsDecided)
					{
						if (menuWindow.SelectedIndex.Equals((int)Enum.MenuItem.セーブ))
						{
							#region 保存内容
							SaveData SD = new SaveData();
							SD.Title = system.ProjectData.GameTitle;
							SD.CommandList = this.commandList;
							SD.LineNumber = this.commandIndex;
							SD.VariableList = this.variableList;
							SD.Sprite = new SaveSprite[this.spriteArray.Length];
							int i = 0;
							foreach (SpriteList sprite in this.spriteArray)
							{
								SD.Sprite[i] = new SaveSprite();
								SD.Sprite[i].InitSaveSprite(sprite);
								i++;
							}

							SD.BgSprite = new SaveSprite();
							SD.BgSpriteFront = new SaveSprite();
							SD.BgSystemSprite = new SaveSprite();
							SD.BgSprite.InitSaveSprite(this.bgSprite);
							SD.BgSpriteFront.InitSaveSprite(this.bgSpriteFront);
							SD.BgSystemSprite.InitSaveSprite(this.bgSystemSprite);
							SD.BgmFile = bgm.URL;
							SD.BgmStart = SaveBgmStart;
							SD.WaitCount = this.waitCount;
							SD.TransitionDuration = this.transitionDuration;
							SD.TransitionCount = this.transitionCount;
							SD.TransitionPreparing = this.transitionPreparing;
							SD.KeyWaiting = this.keyWaiting;
							SD.ClickableWaiting = this.clickableWaiting;
							SD.MenuWaiting = this.menuWaiting;
							SD.SaveMessageOption = this.SaveMessageOption;
							SD.SaveBackColor = this.SaveBackColor;
							#endregion
							// コマンドリストをバイナリでファイルに書き込む
							using (FileStream fs = new FileStream(System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "/" + system.ProjectData.GameTitle + "/test.advsave", FileMode.Create, FileAccess.Write))
							{
								BinaryFormatter bf = new BinaryFormatter();
								bf.Serialize(fs, SD);
							}
							this.menuWaiting = false;
							this.choiceWindow.Enable = true;
							menuWindow.Hide();
							this.commandIndex--;
							Evaluate();
						}
						if (menuWindow.SelectedIndex.Equals((int)Enum.MenuItem.ロード))
						{
							if (File.Exists(System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "/" + system.ProjectData.GameTitle + "/test.advsave"))
							{
								FileStream fs = new FileStream(System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "/" + system.ProjectData.GameTitle + "/test.advsave", FileMode.Open, FileAccess.Read);
								// 読み込んで逆シリアル化する
								BinaryFormatter bf = new BinaryFormatter();
								#region 読込内容
								SaveData SD = bf.Deserialize(fs) as SaveData;
								if (system.ProjectData.GameTitle == SD.Title)
								{
									this.commandList = SD.CommandList;
									this.commandIndex = SD.LineNumber;
									this.variableList = SD.VariableList;
									this.spriteArray = new SpriteList[SD.Sprite.Length];
									for (int i = 0; i < SD.Sprite.Length; i++)
									{
										SD.Sprite[i].LoadSprite(ref spriteArray[i]);
									}
									SD.BgSprite.LoadSprite(ref this.bgSprite);
									SD.BgSpriteFront.LoadSprite(ref this.bgSpriteFront);
									if (SD.BgSystemSprite != null)
									{
										// 読み込む
										bgSystemGraphics.FillRectangle(new SolidBrush(SD.SaveBackColor), bgSystemGraphics.VisibleClipBounds);
										bgSystemSprite.Load_Bitmap(bgSystemBitmap);
										bgSystemSprite.DblAlpha = SD.BgSystemSprite.alpha[0];
										bgSystemSprite.DestSize.SetSize(system.Screen.Width, system.Screen.Height);
										bgSystemSprite.FadeinDuration = SD.BgSystemSprite.fadeinDuration[0];
										bgSystemSprite.FadeoutDuration = SD.BgSystemSprite.fadeoutDuration[0];
										bgSystemSprite.FadeinCount = SD.BgSystemSprite.fadeinCount[0];
										bgSystemSprite.FadeoutCount = SD.BgSystemSprite.fadeoutCount[0];
									}
									else
									{
										system.Screen.SetClearColor(SD.SaveBackColor.R, SD.SaveBackColor.G, SD.SaveBackColor.B);
									}
									if (SD.BgmFile != string.Empty)
									{
										if (!File.Exists(SD.BgmFile))
										{
											throw new FileNotFoundException("ファイル " + SD.BgmFile + " は存在しません。", SD.BgmFile);
										}
										// ファイル名が違うときだけ読み込む
										bgm.URL = SD.BgmFile;
										if (SD.BgmStart)
										{ bgm.controls.play(); }
										SaveBgmStart = SD.BgmStart;
									}
									this.waitCount = SD.WaitCount;
									this.transitionDuration = SD.TransitionDuration;
									this.transitionCount = SD.TransitionCount;
									this.transitionPreparing = SD.TransitionPreparing;
									this.keyWaiting = SD.KeyWaiting;
									this.clickableWaiting = SD.ClickableWaiting;
									this.menuWaiting = SD.MenuWaiting;
									messageWindow.SetParams(SD.SaveMessageOption);
									choiceWindow.SetParams(SD.SaveMessageOption);
									menuWindow.SetParams(SD.SaveMessageOption);
								}
								#endregion
							}
							this.menuWaiting = false;
							this.choiceWindow.Enable = true;
							menuWindow.Hide();
							this.commandIndex--;
							Evaluate();
						}
						if (menuWindow.SelectedIndex.Equals((int)Enum.MenuItem.閉じる))
						{
							this.menuWaiting = false;
							this.choiceWindow.Enable = true;
							menuWindow.Hide();
							this.commandIndex--;
							Evaluate();
						}
						if (menuWindow.SelectedIndex.Equals((int)Enum.MenuItem.終了))
						{
							system.TaskController.Terminate();
							system.SceneController.ExitScene();
						}
					}
				}
			}
			// Tabキーが押されたらメニューを表示する
			// TODO:(トランジション中は表示しない)背景色トラジションでエラーが出る為
			else if (system.Keyboard.IsPress(KeyCode.TAB) && transitionCount < 0)
			{
				this.menuWaiting = true;
				this.choiceWindow.Enable = false;
				this.menuWindow.Show(Const.MenuItems);
			}

            // メッセージウィンドウの転送
            // （最後に転送しないとトランジションがうまくいかない）
            messageWindow.Update(system);
            choiceWindow.Update(system);
			menuWindow.Update(system);

            return 0;
        }


        /// <summary>
        /// 描画処理（メッセージウィンドウは除く）
        /// </summary>
        private void UpdateGraphics() {

            // トランジション準備中
            if (transitionPreparing) {
                // 準備前の画面だけを表示する
                system.Screen.Blt(capturedTexture, 0, 0);
                return;
            }

            // 背景の転送
            bgSystemSprite.Update(system);
            bgSprite.Update(system);
            bgSpriteFront.Update(system);
            // 立ち絵テクスチャの転送
            // z座標の小さい順に並べてあるので、その順に転送すればz座標が大きいものが最前面に来る
            foreach (SpriteList spriteList in spriteArray)
            {
				if (spriteList == null)
				{ continue; }
                foreach (MySprite s in spriteList)
                {
                    s.Update(system);
                }
            }

			// ボタン描画
            try {
                control.OnPaint(system.Screen);
            } catch { }

            // トランジション実行中
            // 前面に、準備のときに保存した画面を、後面に現在のテクスチャを表示する
            if (transitionCount >= 0) {
                int alpha = 255 - (int)((255.0 / transitionDuration) * (transitionDuration - transitionCount));
                // 前面(トランジション準備時の画面)をだんだん薄く
                system.Screen.SetColor(255, 255, 255, alpha);
                system.Screen.Blt(capturedTexture, 0, 0);
                system.Screen.ResetColor();
            }
		}

    }
}
